/**
 * 
 */
package cz.cuni.mff.abacs.burglar.logics.objects.positions;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import cz.cuni.mff.abacs.burglar.logics.DataMap;
import cz.cuni.mff.abacs.burglar.logics.objects.BaseInterface;
import cz.cuni.mff.abacs.burglar.logics.objects.BaseObject;
import cz.cuni.mff.abacs.burglar.logics.objects.Room;
import cz.cuni.mff.abacs.burglar.logics.objects.items.Inventory;
import cz.cuni.mff.abacs.burglar.logics.objects.items.Item;


/**
 * 
 * 
 * @author abacs
 *
 */
public class Container extends BasePosition 
		implements Inventory, Position, Lockable {
	
	
	/**
	 * Changeable property. */
	protected final Map<Integer, Item> _inventory = 
		new HashMap<Integer, Item>();
	
	/**
	 * Changeable property. */
	protected boolean _isLocked = false;
	
	/**
	 * Changeable property. */
	protected boolean _isClosed = false;
	
	/**
	 * Changeable property. */
	protected boolean _isRigged = true;
	
	/** 
	 * Not changeable property. */
	protected final int _keyId;
	
	
	// -------------------------------------------------------------------------
	// constructors:
	
	
	/**
	 * 
	 * 
	 * @param id
	 * @param x
	 * @param y
	 * @param room
	 * @param isClosed
	 * @param referenceMap
	 */
	public Container(
			int id,
			int x,
			int y,
			Room room,
			boolean isClosed,
			DataMap referenceMap
	) {
		super(id, BaseObject.Type.CONTAINER, x, y, false, room, referenceMap);
		
		this._isClosed = isClosed;
		this._isLocked = false;
		this._keyId = -1;
	}
	
	
	/**
	 * 
	 * 
	 * @param id
	 * @param x
	 * @param y
	 * @param roomId
	 * @param isClosed
	 * @param referenceMap
	 */
	public Container(
			int id,
			int x,
			int y,
			int roomId,
			boolean isClosed,
			DataMap referenceMap
	) {
		super(id, BaseObject.Type.CONTAINER, x, y, false, roomId, referenceMap);
		
		this._isClosed = isClosed;
		this._isLocked = false;
		this._keyId = -1;
		
	}
	
	
	/**
	 * 
	 * @param id
	 * @param x
	 * @param y
	 * @param room
	 * @param isClosed
	 * @param isLocked
	 * @param keyId
	 * @param referenceMap
	 */
	public Container(
			int id,
			int x,
			int y,
			Room room,
			boolean isClosed,
			boolean isLocked,
			int keyId,
			DataMap referenceMap
	) {
		super(id, BaseObject.Type.CONTAINER, x, y, false, room, referenceMap);
		this._isClosed = isClosed;
		this._isLocked = isLocked;
		this._keyId = keyId;
		
	}
	
	
	/**
	 * 
	 * @param id
	 * @param x
	 * @param y
	 * @param room
	 * @param isClosed
	 * @param isLocked
	 * @param keyId
	 * @param referenceMap
	 */
	public Container(
			int id,
			int x,
			int y,
			int roomId,
			boolean isClosed,
			boolean isLocked,
			int keyId,
			DataMap referenceMap
	) {
		super(id, BaseObject.Type.CONTAINER, x, y, false, roomId, referenceMap);
		this._isClosed = isClosed;
		this._isLocked = isLocked;
		this._keyId = keyId;
		
	}
	
	
	/**
	 * 
	 * @param other
	 * @param referenceMap
	 */
	protected Container(Container other, DataMap referenceMap) {
		super(other, referenceMap);
		this._isClosed = other._isClosed;
		this._isLocked = other._isLocked;
		this._keyId = other._keyId;
	}
	
	
	// -------------------------------------------------------------------------
	// Inventory:
	
	
	@Override
	public void addItem(Item item) {
		this._inventory.put(new Integer(item.getId()), item);
	}
	
	
	@Override
	public void addItems(List<Item> items) {
		for(Item item : items){
			if(this.hasItem(item.getId()))
				continue;
			this.addItem(item);
		}
	}
	
	
	@Override
	public Item removeItem(int id) {
		Item ret = this._inventory.get(id);
		this._inventory.remove(id);
		return ret;
	}
	
	
	@Override
	public boolean hasItem(int id) {
		if(this._inventory.get(id) != null)
			return true;
		return false;
	}
	
	
	@Override
	public List<Item> getItems() {
		List<Item> ret = new ArrayList<Item>();
		for(Item item : this._inventory.values()){
			ret.add(item);
		}
		return ret;
	}
	
	
	// -------------------------------------------------------------------------
	// Lockable:
	
	
	@Override
	public boolean open() {
		if(this._isLocked == false || this._isClosed == false){
			this._isClosed = false;
			return true;
		}
		return false;
	}
	
	
	@Override
	public boolean close() {
		this._isClosed = true;
		return true;
	}

	
	@Override
	public boolean unlock(int key_id) {
		if(this._isLocked == false)
			return true;
		if(this._keyId == key_id){
			this._isLocked = false;
			return true;
		}
		return false;
	}
	
	
	@Override
	public boolean lock(int key_id) {
		if(this._isClosed == false)
			return false;
		if(this._isLocked)
			return true;
		if(this._keyId == key_id){
			this._isLocked = true;
			return true;
		}
		return false;
	}
	
	
	@Override
	public int getKeyId() {
		return this._keyId;
	}
	
	
	@Override
	public boolean isClosed() {
		return this._isClosed;
	}

	
	@Override
	public boolean isLocked() {
		return this._isLocked;
	}
	
	
	// -------------------------------------------------------------------------
	
	
	@Override
	public boolean isOperablePosition() {
		return true;
	}
	
	
	@Override
	public boolean isTrapPosition() {
		return false;
	}
	
	
	// -------------------------------------------------------------------------
	
	
	/**
	 * From far the agent can see whether the container is opened.
	 * It can't see it's contents, or whether it's locked.
	 */
	@Override
	public Position examineFromFar() {
		return new Container(this.getId(), this.getX(), this.getY(), this._roomId, this._isClosed, null);
	}
	
	
	/**
	 * From near the agent can see whether the container is opened.
	 * It can see it's contents, only if it is opened.
	 * It can see whether it's locked.
	 */
	@Override
	public Position examineFromNear() {
		Container container = new Container(this.getId(), this.getX(), this.getY(), this._roomId, this._isClosed, this._isLocked, this._keyId, null);
		
		if(this._isClosed == false){
			for(Item item : this._inventory.values()){
				container.addItem(item);
			}
		}
		
		return container;
	}
	
	
	@Override
	public boolean matches(Position position) {
		if(super.matches(position) == false)
			return false;
		
		Container container = (Container) position;
		if(this._isClosed != container._isClosed || this._isLocked != container._isLocked || this.getKeyId() != container.getKeyId())
			return false;
		
		for(Item item : container.getItems())
			if(this.hasItem(item.getId()) == false)
				return false;
		
		for(Item item : this.getItems())
			if(container.hasItem(item.getId()) == false)
				return false;
		
		return true;
	}
	
	
	@Override
	public void copyChangeables(Position original) {
		assert(original.isTypeOf(BaseInterface.Type.CONTAINER));
		Container original_ =  (Container)original;
				
		this._inventory.clear();
		for(Item item : original_._inventory.values()){
			this._inventory.put(item.getId(), item);
		}
		
		this._isLocked = original_._isLocked;
		
		this._isClosed = original_._isClosed;
		
		this._isRigged = original_._isRigged;
	}
	
	
	@Override
	public Position copy(DataMap reference) {
		return new Container(this, reference);
	}
	
	
}
